package com.motu.vertx.module.utility.toolset;



import com.google.protobuf.Message.Builder;
import com.googlecode.protobuf.format.JsonFormat;
import com.motu.vertx.module.utility.webtool.EncryptTool;
import io.vertx.core.*;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.ServerWebSocket;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.client.*;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.*;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.X509EncodedKeySpec;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.Deflater;
import java.util.zip.Inflater;

//import java.security.NoSuchAlgorithmException;
//import java.security.NoSuchAlgorithmException;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;






//import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin;

//@SuppressWarnings("restriction")
@SuppressWarnings("restriction")
public class Tool {

	public static final Logger logger = LoggerFactory.getLogger("Tool");

	public static final int MINUTE = 60;
	public static final int HOUR = 60 * MINUTE;
	public static final int DAY = 24 * HOUR;
	public static final int WEEK = 7 * DAY;
	public static final int CMD_EXECUTE_TIME_MAX = 2000;  //命令执行最大时间（毫秒）

	/***
	 * 字符串转int数组
	 * @param value 字符串
	 * @return int数组
	 */
	public static int[] strToIntArray(String value,String c) {
		String[] strArray = value.replace(" ", "").split(c);// 去除空格
		int[] intArray = new int[strArray.length];
		for (int i = 0; i < strArray.length; i++) {
			intArray[i] = Integer.parseInt(strArray[i]);
		}
		return intArray;
	}
	/***
	 * 字符串转int数组
	 * @param value 字符串
	 * @return int数组
	 */
	public static Integer[] strToIntegerArray(String value,String c) {
		String[] strArray = value.replace(" ", "").split(c);// 去除空格
		Integer[] intArray = new Integer[strArray.length];
		for (int i = 0; i < strArray.length; i++) {
			intArray[i] = Integer.parseInt(strArray[i]);
		}
		return intArray;
	}
	/***
	 * 字符串转int数组
	 * @param value 字符串
	 * @return int数组
	 */
	public static double[] strToDoubleArray(String value,String c) {
		String[] strArray = value.replace(" ", "").split(c);// 去除空格
		double[] doubleArray = new double[strArray.length];
		for (int i = 0; i < strArray.length; i++) {
			doubleArray[i] = Double.parseDouble(strArray[i]);
		}
		return doubleArray;
	}

	/***
	 * 字符串转long数组
	 * @param value 字符串
	 * @return long数组
	 */
	public static long[] strToLongArray(String value,String c) {
		String[] strArray = value.replace(" ", "").split(c);// 去除空格
		long[] longArray = new long[strArray.length];
		for (int i = 0; i < strArray.length; i++) {
			longArray[i] = Long.parseLong(strArray[i]);
		}
		return longArray;
	}

	/***
	 * int数组转list
	 * @param array 数组
	 * @return list
	 */
	public static List<Integer> intArrayToList(int[] array){
		List<Integer> list = new ArrayList<Integer>();
		for (int i = 0;i < array.length;i++){
			list.add(array[i]);
		}
		return list;
	}

	/***
	 * 根据两个数组来取得一个配置好的随机值
	 * @param value 数值组
	 * @param rate 概率组
	 * @param randMax 概率是百分比还是万分比
	 * @return 随机值
	 */
	public static int getRandomInArray(int[] value,int[] rate,int randMax){
		int count = 0;
		for(int i=0;i<value.length;i++){
			int rand = Tool.getIntRandom(randMax);
			count += rate[i];
			if(rand < count){
				return value[i];
			}
		}

		return 0;
	}

	/**
	 * 计算概率数组随机概率
	 * @param pers 概率数组
	 * @return
	 */
	public static int getRandomIndexByRateArray(int[] pers){
		int allRate = 0;
		for(int per : pers){
			allRate += per;
		}
		int thisRd = Tool.getRandom(1, allRate);
		int rd = 0;
		for(int i = 0;i < pers.length;i++){
			rd += pers[i];
			if(thisRd <= rd){
				return i;
			}
		}
		return 0;
	}

	/**
	 * 计算概率数组随机概率
	 * @param pers 概率数组
	 * @return
	 */
	public static int getRandomIndexByRateArray(List<Integer> pers){
		int allRate = 0;
		for(int per : pers){
			allRate += per;
		}
		int thisRd = Tool.getRandom(1, allRate);
		int rd = 0;
		for(int i = 0;i < pers.size();i++){
			rd += pers.get(i);
			if(thisRd <= rd){
				return i;
			}
		}
		return 0;
	}

	/**
	 * 将一个int转型成string
	 * @param value 整型值
	 * @return 转化后的字符串
	 */
	public static String intToString(int value) {
		return String.valueOf(value);
	}
	/***
	 * 将浮点数转换成字符串
	 * @param value
	 * @return
	 */
	public static String floatToString(float value) {
		return String.valueOf(value);
	}

	/**
	 * 将一个string转型成无符号int
	 * @param str 要转换的字符串
	 * @return -1表示转换失败，>=0表示转换成功
	 */
	public static int stringToInt(String str) {
		if (str == null) {
			return -1;
		}
		int i = -1;
		try {
			i = Integer.parseInt(str);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return i;
	}

	/**
	 * 取一个闭区间[start,end]内的随机数
	 * @return -1表示转换失败，>=0表示转换成功
	 */
	public static int getRandom(int start, int end) {
		Random rand = new Random();
		int a = start + rand.nextInt(end - start + 1);
		return a;
	}
	public static long getRandomLong(long start, long end) {
		Random rand = new Random();
		long a = start + Math.abs(rand.nextLong())%(end - start + 1);
		return a;
	}
	/***
	 * 获取随机数，从0开始
	 * @param count 随机数个数，如count=100,则返回0-99中的一个
	 * @return 随机数
	 */
	public static int getIntRandom(int count) {// 随机整数[0，99]
		Random rand = new Random();
		return rand.nextInt(count);
	}
	/***
	 * 检查概率是否命中，使用百分比整数概率
	 * @param rate 概率值
	 * @return 是否命中
	 */
	public static boolean checkRandom(int rate) {// 随机整数[0，99]
		int count = 100;
		Random rand = new Random();
		int a = rand.nextInt(count);
		return a < rate;
	}
	/***
	 * 获取100以内的随机浮点数
	 * @return 随机数
	 */
	public static float getFloatRandom() {// 随机浮点数[0.0f,100.0f)
		Random rand = new Random();
		float a = Math.abs(rand.nextFloat()) * 100;
		return a;
	}

	/**
	 * 生成随机偶数
	 * @param count 需要偶数
	 * @return
	 */
	public static int getEventIntRandom(int count) {
		// 创建一个随机数生成器对象
		Random random = new Random();
		// 生成随机偶数
		int evenNumber = random.nextInt(count / 2) * 2;
		return evenNumber;
	}

	/***
	 * 判断一个字符串是否为整数数字
	 * @param str 字符串
	 * @return 判断结果
	 */
	public static boolean isNum(String str) {
		int len = str.length();
		if (len == 0) {
			return false;
		}
		for (int i = 0; i < len; i++) {
			if (!Character.isDigit(str.charAt(i))) {
				return false;
			}
		}
		return true;
	}

	/***
	 * 获取随机16位的密码，由数字和大小写字母组成
	 * @return 密码字符串
	 */
	public static String getRandomPassword() {
		String pwd = "";
		for (int i = 0; i < 16; i++) {
			int a = getRandom(0, 61);
			if (a <= 9) {// 数字
				pwd += intToString(a);
			} else if (a < 36) {
				pwd += (char) (a + 55);// 大写字母
			} else {
				pwd += (char) (a + 61);// 小写字母
			}
		}
		return pwd;
	}

	/**
	 * 获取 排除了list中ASCII值, 随机 num位 的字符串，由数字和大小写字母组成
	 * @param num
	 * @param list 需要排除的ASCII编码列表
	 * @return
	 */
	public static String getRandomExclude(int num,JsonArray list) {
		String result = "";
		for (int i = 0; i < num; i++) {
			result += randomExcludeToString(list);
		}
		return result;
	}

	/**
	 * 随机生成排除list中ASCII值,并转化为string
	 * @param list
	 * @return
	 */
	public static String randomExcludeToString(JsonArray list) {
		String randomString = "";
		int randomInt = 0;
		int a = getRandom(0, 61);
		if (a <= 9) {// 数字
			randomInt = a + 48;
			randomString = intToString(a);
		} else if (a < 36) {
			randomInt = a + 55;
			randomString = String.valueOf((char) (a + 55));// 大写字母
		} else {
			randomInt = a + 61;
			randomString = String.valueOf((char) (a + 61));// 小写字母
		}
		for (int i = 0; i < list.size(); i++) {
			if (randomInt == list.getInteger(i)) {
				return randomExcludeToString(list);
			}
		}
		return randomString;
	}

	/***
	 * 小数点向上取整
	 * @param value 浮点数
	 * @return 取整后的数值
	 */
	public static int ceil(double value) {
		if (value == 0)
			return 0;
		int k = (int) Math.ceil(value);
		k = Math.max(1, k);
		return k;
	}
	/***
	 * 小数点四舍五入
	 * @param value 浮点数
	 * @return 四舍五入后的整数
	 */
	public static int rint(double value) {
		return new BigDecimal(value).setScale(0,  RoundingMode.HALF_UP).intValue();
	}
	/***
	 * 小数点四舍五入
	 * @param value 浮点数
	 * @return 四舍五入后的长整数
	 */
	public static long rlong(double value) {
		return new BigDecimal(value).setScale(0,  RoundingMode.HALF_UP).longValue();
	}
	/***
	 * 判断3个整数是否相等
	 * @param value1 数字1
	 * @param value2 数字2
	 * @param value3 数字3
	 * @return
	 */
	public static boolean equal3(int value1, int value2, int value3) {
		return value1 == value2 && value1 == value3;
	}
	/***
	 * 将一个整数转换成字节数组
	 * @param value 整数
	 * @param size 数组长度
	 * @return 数组，以int[]返回
	 */
	public static int[] getBinaryArray(int value, int size) {
		int tmp[] = new int[size];
		String str = Integer.toBinaryString(value);
		int len = str.length();
		for (int i = size - 1; i >= 0; i--) {
			if (i <= len - 1) {
				int index = len - 1 - i;
				char c = str.charAt(index);
				tmp[i] = (c == '0' ? 0 : 1);
			} else {
				tmp[i] = 0;
			}
		}
		return tmp;
	}
	/***
	 * 将一个长整数转换成字节数组
	 * @param value 长整数
	 * @param size 数组长度
	 * @return 数组，以int[]返回
	 */
	public static int[] getBinaryArray(long value, int size) {
		int tmp[] = new int[size];
		String str = Long.toBinaryString(value);
		int len = str.length();
		for (int i = size - 1; i >= 0; i--) {
			if (i <= len - 1) {
				int index = len - 1 - i;
				char c = str.charAt(index);
				tmp[i] = (c == '0' ? 0 : 1);
			} else {
				tmp[i] = 0;
			}
		}
		return tmp;
	}
	/***
	 * 将二进制的数组转换成整数
	 * @param array 二进制数组
	 * @return 整数
	 */
	public static int getBinaryInt(int[] array) {
		String str = new String();
		for (int i = array.length - 1; i >= 0; i--) {
			str += (array[i] == 0 ? "0" : "1");
		}
		return Integer.parseInt(str, 2);
	}
	/***
	 * 将二进制的数组转换成长整数
	 * @param array 二进制数组
	 * @return 长整数
	 */
	public static long getBinaryLong(int[] array) {
		String str = new String();
		for (int i = array.length - 1; i >= 0; i--) {
			str += (array[i] == 0 ? "0" : "1");
		}
		return Long.parseLong(str, 2);
	}
	/***
	 * 将字符串中的苹果表情符过滤掉
	 * @param name 源字符串
	 * @return 过滤后的字符串
	 */
	public static String dropFourByte(String name) {
		byte[] bytes = null;
		try {
			bytes = name.getBytes("utf-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		byte[] bytes2 = dropFourByte(bytes, 0);
		try {
			name = new String(bytes2, "utf-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return name;
	}
	/***
	 * 屏蔽# $
	 */
	public static String replaceColorChar(String str) {
		String tmp = str.replace("#", "*");
		tmp = tmp.replace("$", "*");
		return tmp;
	}
	/***
	 * 将字节数组中的苹果表情符过滤掉
	 * @param bytes 字节数组
	 * @param index 索引
	 * @return 结果
	 */
	private static byte[] dropFourByte(byte[] bytes, int index) {
		if (bytes == null) {
			return null;
		}
		if (index >= bytes.length) {
			return bytes;
		}
		byte[] byres_re = null;
		byte b = bytes[index];
		byte c = (byte) ((b & 0xff) >>> 7);
		if (c == 0) {
			byres_re = dropFourByte(bytes, index + 1);
			return byres_re;
		}
		c = (byte) ((b & 0xff) >>> 5);
		if (c == 6) {
			byres_re = dropFourByte(bytes, index + 2);
			return byres_re;
		}
		c = (byte) ((b & 0xff) >>> 4);
		if (c == 14) {
			byres_re = dropFourByte(bytes, index + 3);
			return byres_re;
		}
		if (c == 15) {
			byte[] temp = new byte[bytes.length - 4];
			System.arraycopy(bytes, 0, temp, 0, index);
			if (index + 4 >= bytes.length) {
			} else {
				System.arraycopy(bytes, index + 4, temp, index, bytes.length - 4 - index);
			}
			byres_re = dropFourByte(temp, index);
			return byres_re;
		}
		return bytes;
	}
	/***
	 * 判断字符串中是否含有苹果表情符
	 * @param name 字符串
	 * @return 判断结果
	 */
	public static boolean checkFourByte(String name) {

		byte[] bytes = null;
		try {
			bytes = name.getBytes("utf-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		boolean f = checkFourByte(bytes, 0);
		return f;
	}
	/***
	 * 判断字节数组中是否含有苹果表情符
	 * @param bytes 字节数组
	 * @param index 索引
	 * @return 判断结果
	 */
	private static boolean checkFourByte(byte[] bytes, int index) {
		if (bytes == null) {
			return false;
		}
		if (index >= bytes.length) {
			return true;
		}
		byte b = bytes[index];
		byte c = (byte) ((b & 0xff) >>> 7);
		if (c == 0) {
			boolean f = checkFourByte(bytes, index + 1);
			return f;
		}
		c = (byte) ((b & 0xff) >>> 5);
		if (c == 6) {
			boolean f = checkFourByte(bytes, index + 2);
			return f;
		}
		c = (byte) ((b & 0xff) >>> 4);
		if (c == 14) {
			boolean f = checkFourByte(bytes, index + 3);
			return f;
		}
		if (c == 15) {
			return false;
		}
		return false;
	}

	/***
	 * 验证字符串（只能输入中文、字母和数字、韩文、俄文）
	 * @param str 字符串
	 * @return 符合返回true，否则false
	 */
	public static boolean checkStr1(String str) {
		Pattern p = Pattern.compile("^[A-Za-z0-9\u4e00-\u9fa5\u3130-\u318F\uAC00-\uD7A3\u0400-\u052F,，.。、/?？~!！@#￥%……^&*()（）_——=+{}【】;；:：'‘\"”^]{0,}$");
		Matcher m = p.matcher(str);
		if (m.find()) {
			return true;
		} else {
			return false;
		}
	}


	/***
	 * 最新获取字符字节数的方法（包含字母数字简体繁体日韩等等）（英文算1个字符，汉字等算2个）
	 */
	public static int getStrByteLen(String name) {
		char[] chars = name.toCharArray();
		int len = 0;
		for (int i = 0; i < chars.length; i++) {
			int k = 0x80;
			if (chars[i] / k == 0 || chars[i] / k == 8 || chars[i] / k == 28) {// 判断一个字符是Ascill字符还是其它字符（如汉，日，韩文字符） (8表示俄文、28泰文)
				len += 1;
			} else {
				len += 2;
			}
		}
		return len;
	}
	/***
	 * 获取繁体字符串的字节长度
	 * @param name 字符串
	 * @return 长度
	 */
	public static int getStrByteLen_TW(String name) {
		int len = 0;
		byte b[];
		try {
			b = name.getBytes("big5");
			// trace("content = "+content+" byte len = "+b.length+" string len = "+content.length());
			len = b.length;
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return len;
	}
	/***
	 * 判断密码是否为ascii组成
	 * @param pwd 密码字符串
	 * @return 判断结果
	 */
	public static boolean checkPwdAscii(String pwd) {
		char[] tmp = pwd.toCharArray();

		for (int i = 0; i < tmp.length; i++) {
			if (tmp[i] < 33 || tmp[i] > 126) {
				return false;
			}
		}
		return true;
	}

	/***
	 * 判断账号是否由数字和大小写字母组成（纯字母，或者字母+数字组合，不能是纯数字）
	 * @param account 账号字符串
	 * @return 判断结果
	 */
	public static boolean checkAccountNoAllNumber(String account) {
		return checkAccount(account) && checkEngChar(account);
	}

	/***
	 * 判断账号是否由数字和大小写字母组成（可以是纯数字，纯字母，或者字母+数字组合）
	 * @param account 账号字符串
	 * @return 判断结果
	 */
	public static boolean checkAccount(String account) {
		char[] tmp = account.toCharArray();

		for (int i = 0; i < tmp.length; i++) {
			char c = tmp[i];

			if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
				continue;
			} else {
				return false;
			}
		}
		return true;
	}
	/***
	 * 判断一个字符串中是否含有英文字符
	 * @param str
	 * @return
	 */
	public static boolean checkEngChar(String str) {
		char[] tmp = str.toCharArray();

		for (int i = 0; i < tmp.length; i++) {
			char c = tmp[i];

			if ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
				return true;
			}
		}
		return false;
	}

	//	/***
	//	 * 对字符串md5加密
	//	 * @param str 字符串
	//	 * @return 加密后的结果
	//	 */
	//
	//	public static final String EncoderByMd5(String str) {
	//		// 确定计算方法
	//		String newstr = "";
	//		MessageDigest md5;
	//		try {
	//			md5 = MessageDigest.getInstance("MD5");
	//			BASE64Encoder base64en = new BASE64Encoder();
	//			// 加密后的字符串
	//			newstr = base64en.encode(md5.digest(str.getBytes("utf-8")));
	//		} catch (NoSuchAlgorithmException e) {
	//			e.printStackTrace();
	//		} catch (UnsupportedEncodingException e) {
	//			e.printStackTrace();
	//		}
	//
	//		return newstr;
	//	}

	/***
	 * 计算几率，满足返回true，不满足返回false
	 * @param percent 百分比（例如80%则传80）
	 * @return true/false
	 */
	public static boolean odds(int percent) {
		int random = Tool.getIntRandom(100);// 获取0——100之间的随机数（0<=random<100）
		if (random >= 0 && random < percent) {
			return true;
		} else {
			return false;
		}
	}

	/***
	 * 计算几率，满足返回true，不满足返回false
	 * @param percent 万分比（例如80%则传8000）
	 * @return true/false
	 */
	public static boolean odds1(int percent) {
		int random = Tool.getIntRandom(10000);// 获取0——10000之间的随机数（0<=random<10000）
		if (random >= 0 && random < percent) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 生成某个范围内不重复的随机数组，生成的随机数包含起始数值和结束数值（适用于范围长度较小的情况下，当范围长度较小时推荐此方法）
	 * @param start 起始数值
	 * @param end 结束数值
	 * @param count 随机数数量
	 * @return 随机数组
	 */
	public static int[] randomNumberNoRepeat1(int start, int end, int count) {
		int[] intRandom = new int[count];// 返回的随机数组
		List<Integer> list = new ArrayList<Integer>();
		for (int i = start; i <= end; i++) {
			list.add(i);
		}
		for (int i = 0; i < count; i++) {
			int randIndex = getIntRandom(list.size());
			intRandom[i] = list.get(randIndex);
			list.remove(randIndex);
		}
		return intRandom;
	}

	/**
	 * 生成某个范围内不重复的随机数组，生成的随机数包含起始数值和结束数值（适用于随机数数量远远小于范围长度的情况下，在这种情况下推荐此方法）
	 * @param start 起始数值
	 * @param end 结束数值
	 * @param count 随机数数量
	 * @return 随机数组
	 */
	public static int[] randomNumberNoRepeat2(int start, int end, int count) {
		if (start > end){
			return new int[0];
		}
		if (count > end - start + 1){
			count = end - start + 1;
		}
		int[] intRandom = new int[count];// 返回的随机数组
		List<Integer> list = new ArrayList<Integer>(); // 生成数据集，用来保存随即生成数，并用于判断
		// Random rd = new Random(System.currentTimeMillis() + seed.getAndIncrement());
		Random rand = new Random();
		while (list.size() < count) {
			int num = rand.nextInt(end - start + 1) + start;
			if (!list.contains(num)) {
				list.add(num); // 往集合里面添加数据。
			}
		}
		/** 给数值赋值 */
		for (int i = 0; i < list.size(); i++) {
			intRandom[i] = list.get(i);
		}
		return intRandom;
	}



	/***
	 * 字符串是否为空
	 * @param str 字符串
	 * @return 是否为空
	 */
	public static boolean isEmpty(String str) {
		if (str == null || str.isEmpty()) {
			return true;
		} else {
			return false;
		}
	}

	/***
	 * 获取中了概率的序号（默认基数10000）
	 * @param rate 概率数组
	 * @return 中了概率的序号
	 */
	public static int getRateIndex(int[] rate) {
		int random = getIntRandom(10000);
		int total = 0;
		for (int i = 0; i < rate.length; i++) {
			total += rate[i];
			if (random < total) {
				return i;
			}
		}
		return -1;
	}

	/***
	 * 获取中了概率的序号
	 * @param rate 概率数组
	 * @param base 基数
	 * @return 中了概率的序号
	 */
	public static int getRateIndex(int[] rate, int base) {
		int random = getIntRandom(base);
		int total = 0;
		for (int i = 0; i < rate.length; i++) {
			total += rate[i];
			if (random < total) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * 根据权重获取数组命中序号
	 */
	public static int getWeightIndex(int[] rate) {
		int max = 0;
		for (int val : rate){
			max += val;
		}
		int random = getIntRandom(max);
		int total = 0;
		for (int i = 0; i < rate.length; i++) {
			total += rate[i];
			if (random < total) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * 根据权重获取数组命中序号
	 */
	public static int getWeightIndex(List<Integer> rate) {
		int max = 0;
		for (int val : rate){
			max += val;
		}
		int random = getIntRandom(max);
		int total = 0;
		for (int i = 0; i < rate.size(); i++) {
			total += rate.get(i);
			if (random < total) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * 根据范围段数值取得对应的数据
	 * @param range 范围
	 * @param value 数据
	 * @param num 次数
	 * @return
	 */
	public static int getNumByCondition(int[] range, int[] value, int num) {
		if (range.length == 0 || value.length == 0){
			return 0;
		}
		int min = 1;
		int max = 0;
		for (int i = 0; i < range.length; i++) {
			max = range[i];
			if (num >= min && num <= max) {
				return value[i];
			}
			min = max + 1;
		}
		return value[value.length - 1];
	}

	/**
	 * 根据范围段数值取得对应的数据
	 * @param range 范围
	 * @param value 数据
	 * @param num 次数
	 * @return
	 */
	public static int getNumByCondition(JsonArray range, JsonArray value, int num) {
		if (range.size() == 0 || value.size() == 0){
			return 0;
		}
		int min = 1;
		int max = 0;
		for (int i = 0; i < range.size(); i++) {
			max = range.getInteger(i);
			if (num >= min && num <= max) {
				return value.getInteger(i);
			}
			min = max + 1;
		}
		return value.getInteger(value.size() - 1);
	}

	/**
	 * 根据范围段数值取得对应的数据
	 * @param range 范围
	 * @param value 数据
	 * @param num 次数
	 * @return
	 */
	public static long getNumByCondition(long[] range, long[] value, long num) {
		if (range.length == 0 || value.length == 0){
			return 0;
		}
		long min = 1;
		long max = 0;
		for (int i = 0; i < range.length; i++) {
			max = range[i];
			if (num >= min && num <= max) {
				return value[i];
			}
			min = max + 1;
		}
		return value[value.length - 1];
	}

	/**
	 * list明细互换
	 * @param list 列表
	 * @param index1 交互序号1
	 * @param index2 交互序号2
	 */
	public static <T> void listExchange(List<T> list, int index1, int index2) {
		T t = list.get(index1);
		list.set(index1, list.get(index2));
		list.set(index2, t);
	}

	/**
	 * 压缩.
	 * @param inputByte 待压缩的字节数组
	 * @return 压缩后的数据
	 * @throws IOException
	 */
	public static byte[] compress(byte[] inputByte) throws IOException {
		int len = 0;
		Deflater defl = new Deflater();
		defl.setInput(inputByte);
		defl.finish();
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		byte[] outputByte = new byte[102400];
		try {
			while (!defl.finished()) {
				// 压缩并将压缩后的内容输出到字节输出流bos中
				len = defl.deflate(outputByte);
				bos.write(outputByte, 0, len);
			}
			defl.end();
		} finally {
			bos.close();
		}
		return bos.toByteArray();
	}

	/**
	 * @param inputByte 待解压缩的字节数组
	 * @return 解压缩后的字节数组
	 * @throws IOException
	 */
	public static byte[] uncompress(byte[] inputByte) throws IOException {
		int len = 0;
		Inflater infl = new Inflater();
		infl.setInput(inputByte);
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		byte[] outByte = new byte[1024];
		try {
			while (!infl.finished()) {
				// 解压缩并将解压缩后的内容输出到字节输出流bos中
				len = infl.inflate(outByte);
				if (len == 0) {
					break;
				}
				bos.write(outByte, 0, len);
			}
			infl.end();
		} catch (Exception e) {
			//
		} finally {
			bos.close();
		}
		return bos.toByteArray();
	}

	/**
	 * MD5加密-32位小写
	 */
	public static String md5encrypt32(String encryptStr) {
		MessageDigest md5;
		try {
			md5 = MessageDigest.getInstance("MD5");
			byte[] md5Bytes = md5.digest(encryptStr.getBytes());
			StringBuffer hexValue = new StringBuffer();
			for (int i = 0; i < md5Bytes.length; i++) {
				int val = ((int) md5Bytes[i]) & 0xff;
				if (val < 16)
					hexValue.append("0");
				hexValue.append(Integer.toHexString(val));
			}
			encryptStr = hexValue.toString();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return encryptStr;
	}
	/***
	 * 获取一个uuid
	 * @return
	 */
	public static String getUUID() {
		String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");
		return uuid;
	}
	/***
	 * 获取错误异常信息
	 * @param e 异常对象
	 * @return 异常信息
	 */
	public static String getException(Exception e){
		StringWriter sw = new StringWriter();
		e.printStackTrace(new PrintWriter(sw, true));
		String errMsg = sw.toString();
		//		int index = errMsg.indexOf("Caused by:");
		//		if(index != -1) {
		//			errMsg = errMsg.substring(index);
		//		}
		return errMsg;
	}
	/***
	 * 获取错误异常信息
	 * @param e 异常对象
	 * @return 异常信息
	 */
	public static String getException(Throwable e){
		StringWriter sw = new StringWriter();
		e.printStackTrace(new PrintWriter(sw, true));
		String errMsg = sw.toString();
		//		int index = errMsg.indexOf("Caused by:");
		//		if(index != -1) {
		//			errMsg = errMsg.substring(index);
		//		}
		return errMsg;
	}
	/***
	 * 判断一个id是否在列表中
	 * @param list 列表
	 * @param id
	 * @return 判断结果
	 */
	public static boolean isInList(JsonArray list,int id){
		for(int i=0;i<list.size();i++){
			if(list.getInteger(i) == id){
				return true;
			}
		}
		return false;
	}
	/***
	 * 判断一个id是否在列表中
	 * @param list 列表
	 * @param id
	 * @return 判断结果
	 */
	public static boolean isInList(JsonArray list,long id){
		for(int i=0;i<list.size();i++){
			if(list.getLong(i) == id){
				return true;
			}
		}
		return false;
	}
	/***
	 * 判断一个id是否在列表中
	 * @param list 列表
	 * @param id
	 * @return 判断结果
	 */
	public static boolean isInList(JsonArray list,String id){
		for(int i=0;i<list.size();i++){
			if(list.getString(i).equalsIgnoreCase(id)){
				return true;
			}
		}
		return false;
	}
	/***
	 * 判断一个id是否在列表中
	 * @param list 列表
	 * @param id
	 * @return 判断结果
	 */
	public static boolean isInList(List<Integer> list,int id){
		for(int i=0;i<list.size();i++){
			if(list.get(i) == id){
				return true;
			}
		}
		return false;
	}

	/***
	 * 判断一个id是否在列表中
	 * @param list 列表
	 * @param id
	 * @return 判断结果
	 */
	public static boolean isInList(List<Long> list,long id){
		for(int i=0;i<list.size();i++){
			if (list.get(i) == id) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 将一个id从列表中删除
	 * @param list
	 * @param id
	 */
	public static void removeFromList(JsonArray list,int id){
		for(int i=0;i<list.size();i++){
			if(list.getInteger(i) == id){
				list.remove(i);
				return;
			}
		}
	}

	/**
	 * 将一个id从列表中删除
	 * @param list
	 * @param id
	 */
	public static void removeFromList(List<Integer> list,int id){
		for(int i=0;i<list.size();i++){
			if(list.get(i) == id){
				list.remove(i);
				return;
			}
		}
	}


	/**
	 * 将一个id从列表中删除
	 * @param list
	 * @param id
	 */
	public static void removeFromList(JsonArray list, long id){
		for(int i=0;i<list.size();i++){
			if(list.getLong(i) == id){
				list.remove(i);
				return;
			}
		}
	}

	public static void removeFromList(JsonArray list, String id){
		if (isEmpty(id)) {
			return;
		}
		for(int i=0;i<list.size();i++){
			if(id.equals(list.getString(i))){
				list.remove(i);
				return;
			}
		}
	}

	/***
	 * 判断一个id是否在redis key列表中
	 * @param list 列表
	 * @param id
	 * @return 判断结果
	 */
	public static boolean isInRedisList(JsonArray list,String id){
		for(int i=0;i<list.size();i++){
			if(list.getString(i).equalsIgnoreCase(id)){
				return true;
			}
		}
		return false;
	}
	/***
	 * 判断一个id是否在列表中
	 * @param list 列表
	 * @param id
	 * @return 判断结果
	 */
	public static boolean isInList(int[] list,int id){
		for(int i=0;i<list.length;i++){
			if(list[i] == id){
				return true;
			}
		}
		return false;
	}
	/***
	 * 判断一个id是否在列表中
	 * @param list 列表
	 * @param id
	 * @return 判断结果
	 */
	public static boolean isInList(byte[] list,byte id){
		for(int i=0;i<list.length;i++){
			if(list[i] == id){
				return true;
			}
		}
		return false;
	}
	/***
	 * 判断一个id是否在列表中
	 * @param list 列表
	 * @param id
	 * @return 判断结果
	 */
	public static boolean isInList(long[] list,long id){
		for(int i=0;i<list.length;i++){
			if(list[i] == id){
				return true;
			}
		}
		return false;
	}
	/***
	 * 将一个string数组转化成int数组
	 * @param array string数组
	 * @return int数组
	 */
	public static JsonArray toIntArray(JsonArray array){
		JsonArray list = new JsonArray();
		for(int i=0;i<list.size();i++){
			list.add(Integer.parseInt(array.getString(i)));
		}
		return list;
	}
	/***
	 * 获取verticle发布的配置
	 * @param config json配置文件
	 * @param name   配置文件中的key名
	 * @param defaultNum 默认文件
	 * @return
	 */
	public static DeploymentOptions getDeploymentOptions(JsonObject config,String name,int defaultNum,boolean isWorker){
		DeploymentOptions option = new DeploymentOptions()
				.setConfig(config)
				.setThreadingModel(isWorker ? ThreadingModel.WORKER : ThreadingModel.EVENT_LOOP)
				.setInstances(config.getInteger(name, defaultNum));
		return  option;
	}
	/***
	 * 分割双关键字userid.itemid类的itemid
	 * @param array key列表
	 * @return 分割后的列表
	 */
	public static JsonArray splitKeyList(JsonArray array){
		JsonArray list = new JsonArray();
		for(int i=0;i<array.size();i++){
			String str = array.getString(i);
			String str1 = str.substring(str.indexOf('.')+1);
			//			System.out.println("str1:"+str1);
			int id = Tool.stringToInt(str1);
			list.add(id);
		}
		return list;
	}
	/***
	 * 分割双关键字如familyid.userid类的userid
	 * @param array key列表
	 * @return 分割后的列表
	 */
	public static JsonArray splitLongKeyList(JsonArray array){
		JsonArray list = new JsonArray();
		for(int i=0;i<array.size();i++){
			String str = array.getString(i);
			String str1 = str.substring(str.indexOf('.')+1);
			//			System.out.println("str1:"+str1);
			long id = Long.parseLong(str1);
			list.add(id);
		}
		return list;
	}

	/***
	 * 将字符串的首字母变成大写
	 * @param name 字符串
	 * @return 首字母变成大写后的字符串
	 */
	public static String captureName(String name) {
		//        char[] cs=name.toCharArray();
		//        cs[0]-=32;
		//        return String.valueOf(cs);
		name = name.substring(0, 1).toUpperCase() + name.substring(1);
		return  name;

	}
	/***
	 * 获取本机内网ip
	 * @return
	 */
	public static String getHostIp(){
		try{
			Enumeration<NetworkInterface> allNetInterfaces = NetworkInterface.getNetworkInterfaces();
			while (allNetInterfaces.hasMoreElements()){
				NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
				Enumeration<InetAddress> addresses = netInterface.getInetAddresses();
				while (addresses.hasMoreElements()){
					InetAddress ip = (InetAddress) addresses.nextElement();
					if (ip != null
							&& ip instanceof Inet4Address
							&& !ip.isLoopbackAddress() //loopback地址即本机地址，IPv4的loopback范围是127.0.0.0 ~ 127.255.255.255
							&& ip.getHostAddress().indexOf(":")==-1){
						//                        System.out.println("本机的IP = " + ip.getHostAddress());
						return ip.getHostAddress();
					}
				}
			}
		}catch(Exception e){
			e.printStackTrace();
		}
		return null;
	}
	/***
	 * 替换掉sql语句中的?，返回实际查询语句
	 * @param sql1
	 * @param params1
	 * @return
	 */
	public static String getSqlString(String sql1,JsonArray params1) {
		String sql = sql1;
		String regex = "\\?";
		for(int i=0;i<params1.size();i++) {
			Object obj = params1.getValue(i);
			if(obj instanceof Integer) {
				sql = sql.replaceFirst(regex,""+(Integer)obj);
			}else if(obj instanceof Long) {
				sql = sql.replaceFirst(regex,""+(Long)obj);
			}else if(obj instanceof BigInteger) {
				sql = sql.replaceFirst(regex,""+obj.toString());
			}else if(obj instanceof Byte) {
				sql = sql.replaceFirst(regex,""+(Byte)obj);
			}else if(obj instanceof Short) {
				sql = sql.replaceFirst(regex,""+(Short)obj);
			}else if(obj instanceof String) {
				sql = sql.replaceFirst(regex,"'"+(String)obj+"'");
			}else if(obj instanceof Float) {
				sql = sql.replaceFirst(regex,""+(Float)obj);
			}else if(obj instanceof Double) {
				sql = sql.replaceFirst(regex,""+(Double)obj);
			} else {
				String type = obj.getClass().getName();
				System.out.println("getSqlString unknown obj type = " + type);
			}
		}
		return sql;
	}

	/**
	 * 验证手机号码
	 * @param phone 手机号码
	 */
	public static boolean checkPhoneNumber(String phone) {
//		String regex = "^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\\d{8}$";
		String regex = "^1[3456789]\\d{9}$";
		if (phone.length() != 11) {
			return false;
		} else {
			Pattern p = Pattern.compile(regex);
			Matcher m = p.matcher(phone);
			boolean isMatch = m.matches();
			return isMatch;
		}
	}

	/**
	 * 验证邮箱
	 *
	 * @param email
	 * @return
	 */
	public static boolean checkEmail(String email) {
		boolean flag = false;
		try {
			String check = "^([a-z0-9A-Z]+[-|_|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
			Pattern regex = Pattern.compile(check);
			Matcher matcher = regex.matcher(email);
			flag = matcher.matches();
		} catch (Exception e) {
			flag = false;
		}
		return flag;
	}


	/**
	 * 将字符串表示的ip地址转换为long表示.
	 *
	 * @param ip ip地址
	 * @return 以32位整数表示的ip地址
	 */
	public static final long ip2long(final String ip) {
		//		if (!RegexpUtils.isExactlyMatches("(\\d{1,3}\\.){3}\\d{1,3}", ip)) {
		//			throw new IllegalArgumentException("[" + ip + "]不是有效的ip地址");
		//		}
		final String[] ipNums = ip.split("\\.");
		return (Long.parseLong(ipNums[0]) << 24)
				+ (Long.parseLong(ipNums[1]) << 16)
				+ (Long.parseLong(ipNums[2]) << 8)
				+ (Long.parseLong(ipNums[3]));
	}
	/***
	 * 将10进制整数形式转换成127.0.0.1形式的ip地址
	 * @param longip
	 * @return
	 */
	public static String long2ip(long longip)
	{
		StringBuffer sb=new StringBuffer("");
		sb.append(String.valueOf(longip>>>24));//直接右移24位
		sb.append(".");
		sb.append(String.valueOf((longip&0x00ffffff)>>>16));//将高8位置0，然后右移16位
		sb.append(".");
		sb.append(String.valueOf((longip&0x0000ffff)>>>8));
		sb.append(".");
		sb.append(String.valueOf(longip&0x000000ff));
		sb.append(".");
		return sb.toString();
	}

	/**
	 * 判断ip地址是否是ipv6
	 * @param ip ip地址
	 * @return 是否是ipv6
	 * @throws Exception 当输入的ip不是一个有效的ip地址会抛出异常
	 */
	public static boolean isIpv6(String ip) throws Exception {
		try {
			InetAddress address = InetAddress.getByName(ip);
			if (address instanceof Inet6Address) {
				return true;
			} else if (address instanceof Inet4Address) {
				return false;
			} else{
				throw new Exception("无效的IP地址:" + ip);
			}
		} catch (UnknownHostException e) {
			throw new Exception("无效的IP地址:" + ip);
		}
	}

	/***
	 * hmac_md5签名
	 * @param srcMsg 待加密的字符串
	 * @param key 加密key
	 * @return
	 * @throws Exception
	 */
	public static String jdkHmacMD5(String srcMsg,String key) {
        try {

        	SecretKey restoreSecretKey = new SecretKeySpec(key.getBytes(), "HmacMd5");
            //实例化Mac
            Mac mac = Mac.getInstance(restoreSecretKey.getAlgorithm());
            //初始化MAC
            mac.init(restoreSecretKey);
            byte[] bytes = mac.doFinal(srcMsg.getBytes());
            String encodedMsg = Hex.encodeHexString(bytes);
            return encodedMsg;
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            e.printStackTrace();
        }
        return null;
    }
	/***
	 * 把jsonobject转成map
	 * @param obj
	 * @return
	 */
	public static Map<String,String> JsonToMap(JsonObject obj){
		 Map<String, String> params = new HashMap<String, String>();
		 for (Iterator<Entry<String, Object>> iter1 = obj.iterator(); iter1.hasNext();) {
	            Entry<String, Object> entry1 = iter1.next();
	            String key = entry1.getKey();
	            String val = (String) entry1.getValue();
	            params.put(key, val);
		 }

		 return params;
	}
	/***
	 * 根据参数名称将除了sign参数外的所有请求参数按照字母先后顺序排序:key=value& .... &key=value 用=号连接参数名与参数值，用&符号连接前后参数对，key和value都要进行urlencode，得到待验字符串
	 * @param params
	 * @param urlencode key和value是否需要urlencode
	 * @return
	 */
	public static String getSignCheckContent(Map<String, String> params,boolean urlencode) {
        if (params == null) {
            return null;
        }
        if(params.containsKey("sign")) {
        	params.remove("sign");
        }
        StringBuffer content = new StringBuffer();
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            if(urlencode) {
            	key = URLEncoder.encode(key, StandardCharsets.UTF_8);
            	value =URLEncoder.encode(value, StandardCharsets.UTF_8);
            }
            content.append((i == 0 ? "" : "&") + key + "=" + value);
        }

        return content.toString();
    }

	public static Map<String,Object> getParamsMap(String str){
		Map<String,Object> pmap = new HashMap<String,Object>();
		try {
			String []tmp = str.split("&");
			for(int i=0;i<tmp.length;i++){
				String[] a = tmp[i].split("=");
				if(a.length >= 2) {
					String name = a[0];
					String value = URLDecoder.decode(a[1],"UTF-8");
//					String value =  a[1];
					pmap.put(name, value);
				}else if(a.length == 1) {//有些第三方回调给的参数只有key没有value,要特殊处理
					String name = a[0];
					String value = "";
					pmap.put(name, value);
				}
			}
		} catch (Exception e) {
			System.out.println("getParamsMap str:"+str);
		}
		return pmap;
	}

	public static Map<String,String> getParamsMap2(String str){
		Map<String,String> pmap = new HashMap<String,String>();
		try {
			String []tmp = str.split("&");
			for(int i=0;i<tmp.length;i++){
				String[] a = tmp[i].split("=");
				if(a.length >= 2) {
					String name = a[0];
					String value = URLDecoder.decode(a[1],"UTF-8");
//					String value =  a[1];
					pmap.put(name, value);
				}else if(a.length == 1) {//有些第三方回调给的参数只有key没有value,要特殊处理
					String name = a[0];
					String value = "";
					pmap.put(name, value);
				}
			}
		} catch (Exception e) {
			System.out.println("getParamsMap str:"+str);
		}
		return pmap;
	}
	/**
	 *参数按字母顺序排序并拼接，通常用于第三方sdk的http get请求参数格式化
	 * @param sortedParams
	 * @return
	 */
	public static String getSignContent(Map<String, String> sortedParams) {
		StringBuffer content = new StringBuffer();
		List<String> keys = new ArrayList<String>(sortedParams.keySet());
		Collections.sort(keys);
		int index = 0;
		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = sortedParams.get(key);
			if (areNotEmpty(key, value)) {
				content.append((index == 0 ? "" : "&") + key + "=" + value);
				index++;
			}
		}
		return content.toString();
	}
	/**
	 * 检查指定的字符串列表是否不为空。
	 */
	public static boolean areNotEmpty(String... values) {
		boolean result = true;
		if (values == null || values.length == 0) {
			result = false;
		} else {
			for (String value : values) {
				result &= !isEmpty(value);
			}
		}
		return result;
	}

	/**
	 * 2个int合并成一个long
	 */
	public static long int2Long(int int1, int int2){
		return ((long)int1 & 0xFFFFFFFFl) | (((long)int2 << 32) & 0xFFFFFFFF00000000l);
	}

	/**
	 * long拆分成2个int
	 */
	public static int[] long2int(long val){
		int[] ret = new int[2];
		ret[0] = (int) (0xFFFFFFFFl & val);
		ret[1] = (int) ((0xFFFFFFFF00000000l & val) >> 32);
		return ret;
	}

	/**
	 * protobuf builder转json
	 */
	public static String protobufToJson(Builder builder){
		return JsonFormat.printToString(builder.build());
	}

	/**
	 * json转protobuf builder
	 */
	public static void jsonToProtobuf(String json, Builder builder){
		try{
			JsonFormat.merge(json, builder);
		}catch (Exception e){
			logger.error("jsonToProtobuf error cause = " + Tool.getException(e));
		}
	}

	/**
	 * 对一字符串进行敏感词过滤处理
	 * @return 过滤后的字符串
	 */
	public static String filterWord(String word) {
		String oldword = word;
		String temp = word.replace(" ", "").replace("&", "").replace("*", "");
		word = SensitivewordEngine.replaceSensitiveWord(temp, SensitivewordEngine.MaxMatchType, "*");
		if (!word.contains("*")){
			word = oldword;
		}
		return word;
	}

	/**
	 * 对一字符串进行敏感词过滤处理(4399渠道)
	 * @return 过滤后的字符串
	 */
	public static String filterWordOf4399(WebClient client, String word) {
		String oldword = word;
		String temp = word.replace(" ", "").replace("&", "").replace("*", "");
		Future<String> future = SensitivewordEngine.replaceSensitiveWordOf4399(client, temp);
		word = future.result();
		if (!word.contains("*")){
			word = oldword;
		}
		return word;
	}

	/**
	 * 发送邮件
	 * @param to 收件人邮箱
	 * @param subject 邮件主题
	 * @param content 邮件内容
	 */
	public static void sendMail(String to, String subject, String content){
		sendMail(to, subject, content, null);
	}

	/**
	 * 发送邮件
	 * @param to 收件人邮箱
	 * @param subject 邮件主题
	 * @param content 邮件内容
	 * @param fromName 发件人名称（没有默认就是发件人邮箱地址）
	 */
	public static void sendMail(String to, String subject, String content, String fromName){
		long startTime = System.currentTimeMillis();
		//设置参数
		Properties properties = new Properties();
		String host = "smtp.feishu.cn";
		properties.setProperty("mail.smtp.host",host);//发送邮箱服务器
		properties.setProperty("mail.smtp.port","465");//发送端口
		properties.setProperty("mail.smtp.auth","true");//是否开启权限控制
//    	properties.setProperty("mail.debug","true");//true 打印信息到控制台
		properties.setProperty("mail.transport","smtp");//发送的协议是简单的邮件传输协议
		properties.setProperty("mail.smtp.ssl.enable","true");
		properties.setProperty("mail.smtp.connectiontimeout","10000");//连接时间限制，单位毫秒。是关于与邮件服务器建立连接的时间长短的。默认是无限制
		properties.setProperty("mail.smtp.timeout","10000");//邮件smtp时间限制，单位毫秒。这个是有关邮件读取时间长短。默认是无限制
		properties.setProperty("mail.smtp.writetimeout","10000");//邮件发送时间限制，单位毫秒。有关发送邮件时内容上传的时间长短。默认同样是无限制
		//建立两点之间的链接
		Session session = Session.getInstance(properties, new Authenticator() {
			@Override
			protected PasswordAuthentication getPasswordAuthentication() {
				return new PasswordAuthentication("gm@more2.cn","Lbe3Jg8nL4dJfqYj");
			}
		});
		//创建邮件对象
		Message message = new MimeMessage(session);
		try {
			//设置发件人
			if (Tool.isEmpty(fromName)){
				message.setFrom(new InternetAddress("gm@more2.cn"));
			} else{
				message.setFrom(new InternetAddress("gm@more2.cn", fromName, "UTF-8"));
			}
			//设置收件人
			message.setRecipient(Message.RecipientType.TO,new InternetAddress(to));
			//设置主题
			message.setSubject(subject);
			//设置邮件正文  第二个参数是邮件发送的类型
			message.setContent(content,"text/html;charset=UTF-8");
			//发送一封邮件
			Transport.send(message);
			long endTime = System.currentTimeMillis();
			logger.info("sendMail to = " + to + " run time = " + (endTime - startTime) + " ms");
		} catch (Exception e) {
			logger.error("sendMail error host = " + host + " to = " + to + " subject = " + subject + " content = " + content + " cause = " + Tool.getException(e));
		}
	}

	/**
	 * 获取拼接字符串
	 * @param values 拼接列表
	 * @return 拼接字符串
	 */
	public static String getJoinString(Object... values) {
		String value = "";
		for (int i = 0; i < values.length; i++) {
			if (value.equals("")) {
				value = String.valueOf(values[i]);
			} else {
				value += "." + values[i];
			}
		}
		return value;
	}

	/**
	 * 对appConfig 加载共用的配置信息
	 * @param appConfig
	 * @param isLocalServer 是否线上路径比本地路径多截取一次/
	 * @return
	 */
	public static JsonObject loadCommonConfig(JsonObject appConfig, boolean isLocalServer) {
		Path path;// 共用配置信息的路径
		if (appConfig.containsKey("common-config-path")) {
			path = Paths.get(appConfig.getString("common-config-path"));
		} else {
			path = Paths.get(System.getProperty("user.dir"));// /Users/ibm/wgx-java/Showbiz/showbiz-server-game
			path = path.getParent();// user.dir为模块启动所在目录，向上一层项目目录
			if (!isLocalServer) {
				path = path.getParent();// 线上多了层remetboot目录要多截取一次
			}
		}
		String commonConfigFileName = isLocalServer ? "common_local.json" : "common.json";
		path = Paths.get(path.toString(), "config", commonConfigFileName);

		BufferedReader reader = null;
		StringBuffer str = new StringBuffer();
		try {
			reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);
			String tempStr;
			while ((tempStr = reader.readLine()) != null) {
				str.append(tempStr);
			}
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		JsonObject jsonObject;
		try {
			jsonObject = new JsonObject(str.toString());// 读取配置文件内容.转为jsonobject
		} catch (Exception e) {
			jsonObject = new JsonObject();
		}

		return jsonObject.mergeIn(appConfig);// 合并对象. 相同key  appconfig 会覆盖jsonobject 的.自定义的配置优先
	}

	/**
	 * 下划线转大写字母（第一个词首字母不大写）
	 * @param str 转换的字符串
	 */
	public static String underlineToUpperCase(String str){
		String[] array = str.split("_");
		String result = "";
		for (int i = 0,size = array.length; i < size; i++) {
			if (result.isEmpty()){
				result = array[i];
			} else{
				result += Tool.captureName(array[i]);
			}
		}
		return result;
	}

	/**
	 * 大写字母转下划线
	 * @param str 转换的字符串
	 */
	public static String upperCaseToUnderline(String str){
		String[] array = str.split("(?=[A-Z])");
		String result = "";
		for (int i = 0; i < array.length; i++) {
			if (result.isEmpty()){
				result = array[i].toLowerCase();
			} else{
				result += "_" + array[i].toLowerCase();
			}
		}
		return result;
	}

	/**
	 * 发送信息到飞书机器人--文本
	 * @param webClient
	 * @param content
	 * @param url 飞书机器人地址:https://open.feishu.cn/open-apis/bot/v2/hook/c6ea470b-c5fd-41f7-97b4-e8e27f5104ac
	 */
	public static Future<JsonObject> sendToFeishu(WebClient webClient, String content, String url) {
		Promise<JsonObject> fut = Promise.promise();
		JsonObject obj = new JsonObject();
		obj.put("msg_type", "text");
		JsonObject contentObj = new JsonObject().put("text", content);
		obj.put("content", contentObj);
		webClient.postAbs(url).timeout(10000L).sendJson(obj, res->{
			if (res.succeeded()) {
				HttpResponse<Buffer> response = res.result();
				int code = response.statusCode();
				JsonObject resObj = response.bodyAsJsonObject();
				fut.complete(resObj);
			} else {
				logger.error("sendToFeishu error content:{} url:{} cause:{}", content, url, res.cause().getMessage());
				fut.fail(res.cause().getMessage());
			}
		});
		return fut.future();
	}

	/**
	 * 发送信息到飞书机器人-富文本
	 * @param webClient
	 * @param msgTitle
	 * @param msgContent
	 * @param url 飞书机器人地址:https://open.feishu.cn/open-apis/bot/v2/hook/c6ea470b-c5fd-41f7-97b4-e8e27f5104ac
	 */
	public static Future<JsonObject> sendPostToFeishu(WebClient webClient,String msgTitle, JsonArray msgContent, String url) {
		Promise<JsonObject> fut = Promise.promise();
		JsonObject obj = new JsonObject();
		obj.put("msg_type", "post");

		JsonObject msgObj = new JsonObject();
		msgObj.put("title",msgTitle);
		msgObj.put("content",new JsonArray().add(msgContent));

		JsonObject postObj = new JsonObject();
		postObj.put("zh_cn",msgObj);

		JsonObject contentObj = new JsonObject().put("post", postObj);
		obj.put("content", contentObj);
		webClient.postAbs(url).timeout(10000L).sendJson(obj, res->{
			if (res.succeeded()) {
				HttpResponse<Buffer> response = res.result();
				int code = response.statusCode();
				logger.info("#### sendPostToFeishu msgTitle:"+msgTitle+" httpCode:"+code+" response:"+response.bodyAsString());
				JsonObject resObj = response.bodyAsJsonObject();
				fut.complete(resObj);
			} else {
				logger.error("sendPostToFeishu error msgTitle = " + msgTitle +  " msgContent = " + msgContent + " url = " + url + " cause = " + res.cause().getMessage());
				fut.fail(res.cause().getMessage());
			}
		});
		return fut.future();
	}

	/**
	 * 判断字符串是否是版本号
	 * @param str 版本号（例如1.0.1）
	 * @return true是 false否
	 */
	public static boolean isVersionStr(String str){
		if (Tool.isEmpty(str)){
			return false;
		}
		String[] array = str.split("\\.");
		if (array.length != 3){
			return false;
		}
		for (int i = 0; i < array.length; i++) {
			if (!Tool.isNum(array[i])){
				return false;
			}
		}
		return true;
	}

	/**
	 * 比较客户端版本号和服务端版本号
	 * @param clientVersion 客户端版本号
	 * @param serverVersion 服务端版本号
	 * @return 客户端版本号>=服务端版本号则为true，否则为false
	 */
	public static boolean compareVersion(String clientVersion, String serverVersion){
		// 非空判断
		if (isEmpty(clientVersion) || isEmpty(serverVersion)) {
			return false;
		}
		if (clientVersion.equals(serverVersion)) {
			return true;
		}
		String[] clientArray = clientVersion.split("\\.");// 1.7.1
		String[] serverArray = serverVersion.split("\\.");// 1.8.0
		for (int i = 0; i < clientArray.length; i++) {
			int client = Integer.parseInt(clientArray[i]);
			int server = Integer.parseInt(serverArray[i]);
			if (client > server){
				return true;
			} else if (client < server){
				return false;
			}
		}
		return false;
	}

	/**
	 * 乘以百分比
	 * @param num 乘数
	 * @param percent 百分比（80%就填写80）
	 * @return 相乘结果取整
	 */
	public static int multiply(int num, int percent){
		return (int)(num * ((double)percent / 100));
	}

	/**
	 * 乘以百分比
	 * @param num 乘数
	 * @param percent 百分比（80%就填写80）
	 * @return 相乘结果取整
	 */
	public static long multiply(long num, int percent){
		return (long)(num * ((double)percent / 100));
	}

	/**
	 * 乘以万分比
	 * @param num 乘数
	 * @param percent 万分比（80%就填写8000）
	 * @return 相乘结果取整
	 */
	public static int multiply2(int num, int percent){
		return (int)(num * ((double)percent / 10000));
	}

	/**
	 * 乘以万分比
	 * @param num 乘数
	 * @param percent 万分比（80%就填写8000）
	 * @return 相乘结果取整
	 */
	public static long multiply2(long num, int percent){
		return (long)(num * ((double)percent / 10000));
	}

	/**
	 * SHA1签名校验方法
	 *
	 * @param content 结果字符串
	 * @param sign 签名字符串
	 * @param publicKey 公钥
	 * @return 是否校验通过
	 */
	public static boolean checkSHA1(String content, String sign, String publicKey) {
		return checkSHA(content, sign, publicKey, "SHA1WithRSA");
	}

	/**
	 * SHA256签名校验方法
	 *
	 * @param content 结果字符串
	 * @param sign 签名字符串
	 * @param publicKey 公钥
	 * @return 是否校验通过
	 */
	public static boolean checkSHA256(String content, String sign, String publicKey) {
		return checkSHA(content, sign, publicKey, "SHA256WithRSA");
	}

	/**
	 * SHA签名校验方法
	 *
	 * @param content 结果字符串
	 * @param sign 签名字符串
	 * @param publicKey 公钥
	 * @param signatureAlgorithm 签名算法字段，可从接口返回数据中获取，例如：OwnedPurchasesResult.getSignatureAlgorithm()
	 * @return 是否校验通过
	 */
	public static boolean checkSHA(String content, String sign, String publicKey, String signatureAlgorithm) {
		if (sign == null) {
			return false;
		}
		if (publicKey == null) {
			return false;
		}

		// 当signatureAlgorithm为空时使用默认签名算法
		if (signatureAlgorithm == null || signatureAlgorithm.length() == 0) {
			signatureAlgorithm = "SHA256WithRSA";
		}
		try {
//            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
			// 生成"RSA"的KeyFactory对象
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			byte[] encodedKey = org.apache.commons.codec.binary.Base64.decodeBase64(publicKey);
			// 生成公钥
			PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
			java.security.Signature signature = null;
			// 根据SHA256WithRSA算法获取签名对象实例
			signature = java.security.Signature.getInstance(signatureAlgorithm);
			// 初始化验证签名的公钥
			signature.initVerify(pubKey);
			// 把原始报文更新到签名对象中
			signature.update(content.getBytes(StandardCharsets.UTF_8));
			// 将sign解码
			byte[] bsign = org.apache.commons.codec.binary.Base64.decodeBase64(sign);
			// 进行验签
			return signature.verify(bsign);
		} catch (Exception e) {
			logger.error("checkSHA error signatureAlgorithm = {} content = {} sign = {} publicKey = {} exception = {}", signatureAlgorithm, content, sign, publicKey, Tool.getException(e));
		}
		return false;
	}

	/**
	 * sha256_HMAC加密
	 * @param message 消息
	 * @param secret  秘钥
	 * @return 加密后字符串
	 */
	public static String sha256_HMAC(String message, String secret) {
		try {
			Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
			SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
			sha256_HMAC.init(secret_key);
			byte[] bytes = sha256_HMAC.doFinal(message.getBytes());
			String hash = EncryptTool.byteToHexString(bytes);
			return hash;
		} catch (Exception e) {
			logger.error("sha256_HMAC error message:{} secret:{} exception:{}", message, secret, Tool.getException(e));
			return "";
		}
	}

	/**
	 * SHA1加密
	 * @param key 加密字符串
	 * @return 加密结果
	 */
	public static String encryptionBySHA1(String key) {
		char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
				'a', 'b', 'c', 'd', 'e', 'f' };
		try {
			MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
			mdTemp.update(key.getBytes("UTF-8"));
			byte[] md = mdTemp.digest();
			int j = md.length;
			char buf[] = new char[j * 2];
			int k = 0;
			for (int i = 0; i < j; i++) {
				byte byte0 = md[i];
				buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
				buf[k++] = hexDigits[byte0 & 0xf];
			}
			return new String(buf);
		} catch (Exception e) {
			logger.error("encryptionBySHA1 error key = {}", key, Tool.getException(e));
			return null;
		}
	}

	/**
	 * SHA1签名校验方法
	 * @param sign 签名字符串
	 * @param keys 目标字符串
	 * @return 是否校验通过
	 */
	public static boolean checkSignBySHA1(String sign,Object... keys ) {
		String data = "";
		for(int i = 0;i < keys.length;i++){
			data += keys[i];
		}
		String encryptionData = encryptionBySHA1(data);
		if (sign.equals(encryptionData)) {
			return true;
		}
		logger.error("checkSHA1 error sign = {} encryptionData = {} keys = {} ", sign, encryptionData, keys);
		return false;
	}

	/**
	 * 判断字符串是否包含中文（中文符号不算）
	 * @param str 字符串
	 * @return true包含 false不包含
	 */
	public static boolean isContainChinese(String str) {
		Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
		Matcher m = p.matcher(str);
		if (m.find()) {
			return true;
		}
		return false;
	}

	/**
	 * 判断字符串是否包含字母
	 * @param str 字符串
	 * @return true包含 false不包含
	 */
	public static boolean isContainLetter(String str) {
		String regex=".*[a-zA-Z]+.*";
		Matcher m=Pattern.compile(regex).matcher(str);
		return m.matches();
	}

	/**
	 * 获取websocket客户端ip
	 */
	public static String getClientIp(ServerWebSocket ws) {
		try{
//			logger.info("websocket headers CF-Connecting-IP:{} X-Forwarded-For:{} X-Real-IP:{}", ws.headers().get("CF-Connecting-IP"),
//					ws.headers().get("X-Forwarded-For"),
//					ws.headers().get("X-Real-IP"));

			String ip = "";
			if(ws.headers().contains("CF-Connecting-IP") ){
				ip = ws.headers().get("CF-Connecting-IP");
				logger.info("websocket getClientIp CF-Connecting-IP:"+ip);
			}
			if (Tool.isEmpty(ip) && ws.headers().contains("X-Forwarded-For")){//亚马逊代理
				// 代理ip可能有2个值，第一个是真实ip，第二个是代理ip（格式：27.55.78.32, 130.176.148.72）
				ip = ws.headers().get("X-Forwarded-For").split(",")[0];
//				logger.info("websocket getClientIp X-Forwarded-For:"+ip);
			}
			if (Tool.isEmpty(ip) && ws.headers().contains("X-Real-IP")){
				//X-Real-IP 需要放在最后面判断（X-Real-IP是通过nginx传递的，如果没有部署nginx，就要优先获取X-Forwarded-For的值）
				ip = ws.headers().get("X-Real-IP");
				logger.info("websocket getClientIp X-Real-IP:"+ip);
			}
			if (Tool.isEmpty(ip)){
				ip = ws.remoteAddress().host();
				logger.info("websocket getClientIp remoteAddress:"+ip);
			}
			return ip;
		}catch(Exception e){
			logger.error("websocket getClientIp error Exception:{}", Tool.getException(e));
			return "";
		}
	}

	/***
	 * 获取http客户端ip（如果经过ngnix转发，则取X-Real-IP参数得到真实ip）
	 * @param routingContext
	 * @return
	 */
	public static String getClientIp(RoutingContext routingContext){
		if(routingContext == null) {
			logger.error("getClientIp routingContext is null");
			return "";
		}
		if(routingContext.request() == null) {
			logger.error("getClientIp routingContext.request() is null");
			return "";
		}
		String ip ="";
		if(routingContext.request().headers() != null ){
//			logger.info("http headers CF-Connecting-IP:{} X-Forwarded-For:{} X-Real-IP:{}", routingContext.request().headers().get("CF-Connecting-IP"),
//					routingContext.request().headers().get("X-Forwarded-For"),
//					routingContext.request().headers().get("X-Real-IP"));

			if(routingContext.request().headers().contains("CF-Connecting-IP")) {
				ip = routingContext.request().headers().get("CF-Connecting-IP");
				logger.info("http getClientIp CF-Connecting-IP:"+ip);
			}
			if (Tool.isEmpty(ip) && routingContext.request().headers().contains("X-Forwarded-For")){
				// 代理ip可能有2个值，第一个是真实ip，第二个是代理ip（格式：27.55.78.32, 130.176.148.72）
				ip = routingContext.request().headers().get("X-Forwarded-For").split(",")[0];
//				logger.info("http getClientIp X-Forwarded-For:"+ip);
			}
			if (Tool.isEmpty(ip) && routingContext.request().headers().contains("X-Real-IP")){
				//X-Real-IP 需要放在最后面判断（X-Real-IP是通过nginx传递的，如果没有部署nginx，就要优先获取X-Forwarded-For的值）
				ip = routingContext.request().headers().get("X-Real-IP");
				logger.info("http getClientIp X-Real-IP:"+ip);
			}
		}
		if (Tool.isEmpty(ip)){
			if(routingContext.request().remoteAddress() == null) {
				logger.error("getClientIp routingContext.request().remoteAddress() is null");
			}else {
				ip = routingContext.request().remoteAddress().host();
				logger.info("http getClientIp remoteAddress:"+ip);
			}
		}
		return ip;
	}
	/***
	 * 获取客户端请求的国家代码
	 * @param routingContext
	 * @return
	 */
	public static String getIpCountry(RoutingContext routingContext){
		if(routingContext == null) {
			logger.error("getIpCountry routingContext is null");
			return "";
		}
		if(routingContext.request() == null) {
			logger.error("getIpCountry routingContext.request() is null");
			return "";
		}
		String code ="";

		if(routingContext.request().headers() != null ){
			if(routingContext.request().headers().contains("Cf-Ipcountry")) {
				code = routingContext.request().headers().get("Cf-Ipcountry");
				logger.info("Cf-Ipcountry:"+code);
			}
		}
		return code;
	}

	/**
	 * 获取一个范围内打乱的随机数列表
	 * @param start
	 * @param end
	 * @return
	 */
	public static List<String> getShuffleArray(int start, int end) {
		// 计算范围内的数字个数
		int length = end - start + 1;
		// 创建一个初始顺序的数组
		List<String> array = new ArrayList<>();
		for (int i = 0; i < length; i++) {
			array.add(String.valueOf(start + i));
		}
		// 打乱数组顺序
		for (int i = 0; i < length; i++) {
			// 生成一个随机索引
			int randomIndex = (int) (Math.random() * length);
			// 交换当前位置和随机位置的元素
			String temp = array.get(i);
			array.set(i, array.get(randomIndex));
			array.set(randomIndex, temp);
		}
		return array;
	}

	/**
	 * 谷歌翻译
	 * @param webClient http请求对象
	 * @param key 谷歌翻译api key
	 * @param word 翻译文字
	 * @param target 目标翻译语言（没有传默认翻译成英文）
	 * @return 翻译结果{"code":0, "result":翻译结果} // code>0表示错误
	 */
	public static Future<JsonObject> googleTranslate(WebClient webClient, String key, String word, String target) throws Exception{
		Promise<JsonObject> promise = Promise.promise();
		if (Tool.isEmpty(target)){
			target = "en";// 没有传翻译语言，默认用英文
		}
		final String finalTarget = target;
		String url = "https://www.googleapis.com/language/translate/v2?q=" + URLEncoder.encode(word, "UTF-8") + "&target=" + target + "&key=" + key;
		webClient.getAbs(url).timeout(10000).send(res->{
			if (res.succeeded()){
				try{
					JsonObject result = res.result().bodyAsJsonObject();
					if (result.containsKey("error")){
						logger.error("doTranslate error target:{} word:{} errorinfo:{}", finalTarget, word, result.getJsonObject("error").toString());
						promise.complete(new JsonObject().put("code", ErrorWord.WRONG_STATUS.value));
					} else{
						/**
						 * {
						 *   "data": {
						 *     "translations": [
						 *       {
						 *         "translatedText": "Hello",
						 *         "detectedSourceLanguage": "zh-CN"
						 *       }
						 *     ]
						 *   }
						 * }
						 */
						logger.info("doTranslate target:{} word:{} result:{}", finalTarget, word, result);
						if (result.containsKey("data")){
							JsonObject data = result.getJsonObject("data");
							if (data.containsKey("translations")){
								JsonArray translations = data.getJsonArray("translations");
								JsonObject translation = translations.getJsonObject(0);
								promise.complete(new JsonObject().put("code", 0).put("result", translation.getString("translatedText")));
							} else{
								promise.complete(new JsonObject().put("code", ErrorWord.NOT_DATA_1.value));
							}
						} else{
							promise.complete(new JsonObject().put("code", ErrorWord.NOT_DATA.value));
						}
					}
				}catch(Exception e){
					logger.error("doTranslate error target:{} word:{} url:{} result:{} cause:{}", finalTarget, word, url, res.result().bodyAsString(), Tool.getException(e));
					promise.complete(new JsonObject().put("code", ErrorWord.LOGIC_EXCEPTION.value));
				}
			} else{
				logger.error("doTranslate error target:{} word:{} cause:{}", finalTarget, word, Tool.getException(res.cause()));
				promise.complete(new JsonObject().put("code", ErrorWord.NET_ERROR_TIMEOUT.value));
			}
		});
		return promise.future();
	}

	/**
	 * 临时加回（部分分支过渡用）
	 */
	@Deprecated
	public static String actWordHashcode(int useType, String... desAry) {
		StringBuilder sb = new StringBuilder(useType);
		for (String str : desAry) {
			sb.append(str);
		}
		return Tool.md5encrypt32(sb.toString()).toLowerCase();
	}

	/**
	 * ActWord hashcode算法，根据参数名称将所有请求参数按照字母先后顺序排序:key=value& .... &key=value 用=号连接参数名与参数值，用&符号连接前后参数对，key和value都要进行urlencode，得到待验字符串
	 * @param desData 格式例{"des_1":"字符串内容","des_2":"字符串内容"}，依此类推
	 * @return md5(useType+内容拼接).toLowerCase()
	 */
	public static String actWordHashcode(int useType, JsonObject desData) {

		Map<String, String> params = new HashMap<String, String>();
		for (Iterator<Entry<String, Object>> iter1 = desData.iterator(); iter1.hasNext();) {
			Entry<String, Object> entry1 = iter1.next();
			String key = entry1.getKey();
			String val = (String) entry1.getValue();
			params.put(key, val);
		}

		StringBuilder content = new StringBuilder()
				.append(useType);
		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);

		for (int i = 0; i < keys.size(); i++) {
			String key = URLEncoder.encode(keys.get(i));
			String value = URLEncoder.encode(params.get(key));
			if (!Tool.isEmpty(value)) {
				content.append((i == 0 ? "" : "&") + key + "=" + value);
			}
		}

		return Tool.md5encrypt32(content.toString()).toLowerCase();
	}

	/**
	 * 调用带参数的静态方法（主要GM重载用）
	 * <p>可用类型，java.lang里的部分基础类型，例如String、int等和部分数组, io.vertx.core.json的JsonObject和JsonArray
	 * <p>例如目标方法dataCheck(Integer param1, int[] param2)，传的params结构例子
	 * <blockquote><pre>
	 *     JsonArray.add(Integer.class.getName()).add(1000)
	 *              .add(int[].class.getName()).add(JsonArray("[1,2,3]"));
	 * </pre></blockquote>
	 *
	 * @param packetPath 包名，例org.demo.
	 * @param className 类名，例Demo
	 * @param staticMethodName 公共静态方法名，例dataCheck
	 * @param params 参数json数组，要按目标方法的参数顺序，例[参数类名1,参数值1,参数类名2,参数值2...]
	 */
	public static void methodInvokeWithParams(String packetPath, String className, String staticMethodName, JsonArray params)
			throws Exception {
		if (packetPath.contains("logichandler")) {
			// 避免此方法调用到协议
			throw new IllegalArgumentException("cant execute 'logichandler' method, packetPath:" + packetPath);
		}
		// 参数生成
		int len = params.size() / 2;
		Class<?>[] parameterTypes = new Class<?>[len];
		Object[] args = new Object[len];
		for (int i = 0; i < len; i++) {
			int typeIdx = i * 2;
			int valueIdx = typeIdx + 1;
			String paramType = params.getString(typeIdx);
			Object arg;
			Class<?> parameterType;
			if (String.class.getName().equals(paramType)) {
				parameterType = String.class;
				arg = params.getString(valueIdx);
			} else if (String[].class.getName().equals(paramType)) {
				parameterType = String[].class;
				arg = params.getJsonArray(valueIdx).stream()
						.map(String::valueOf)
						.toArray(String[]::new);
			} else if (Long.class.getName().equals(paramType)) {
				parameterType = Long.class;
				arg = params.getLong(valueIdx);
			} else if (long.class.getName().equals(paramType)) {
				parameterType = long.class;
				arg = params.getLong(valueIdx).longValue();
			} else if (Long[].class.getName().equals(paramType)) {
				parameterType = Long[].class;
				arg = params.getJsonArray(valueIdx).stream()
						.map(o -> Long.parseLong(o.toString()))
						.toArray(Long[]::new);
			} else if (long[].class.getName().equals(paramType)) {
				parameterType = long[].class;
				arg = params.getJsonArray(valueIdx).stream()
						.mapToLong(o -> Long.parseLong(o.toString()))
						.toArray();
			} else if (Integer.class.getName().equals(paramType)) {
				parameterType = Integer.class;
				arg = params.getInteger(valueIdx);
			} else if (int.class.getName().equals(paramType)) {
				parameterType = int.class;
				arg = params.getInteger(valueIdx).intValue();
			} else if (Integer[].class.getName().equals(paramType)) {
				parameterType = Integer[].class;
				arg = params.getJsonArray(valueIdx).stream()
						.map(o -> (Integer) o)
						.toArray(Integer[]::new);
			} else if (int[].class.getName().equals(paramType)) {
				parameterType = int[].class;
				arg = params.getJsonArray(valueIdx).stream()
						.mapToInt(o -> (int) o)
						.toArray();
			} else if (Byte.class.getName().equals(paramType)) {
				parameterType = Byte.class;
				arg = params.getInteger(valueIdx).byteValue();
			} else if (byte.class.getName().equals(paramType)) {
				parameterType = byte.class;
				arg = params.getInteger(valueIdx).byteValue();
			} else if (Double.class.getName().equals(paramType)) {
				parameterType = Double.class;
				arg = params.getDouble(valueIdx);
			} else if (double.class.getName().equals(paramType)) {
				parameterType = double.class;
				arg = params.getDouble(valueIdx).doubleValue();
			} else if (Float.class.getName().equals(paramType)) {
				parameterType = Float.class;
				arg = params.getFloat(valueIdx);
			} else if (float.class.getName().equals(paramType)) {
				parameterType = float.class;
				arg = params.getFloat(valueIdx).floatValue();
			} else if (Boolean.class.getName().equals(paramType)) {
				parameterType = Boolean.class;
				arg = params.getBoolean(valueIdx);
			} else if (boolean.class.getName().equals(paramType)) {
				parameterType = boolean.class;
				arg = params.getBoolean(valueIdx).booleanValue();
			} else if (io.vertx.core.json.JsonObject.class.getName().equals(paramType)) {
				parameterType = JsonObject.class;
				arg = params.getJsonObject(valueIdx);
			} else if (io.vertx.core.json.JsonArray.class.getName().equals(paramType)) {
				parameterType = JsonArray.class;
				arg = params.getJsonArray(valueIdx);
			} else {
				throw new java.lang.IllegalArgumentException("unsupported paramType = " + paramType);
			}
			parameterTypes[i] = parameterType;
			args[i] = arg;
		}
		// 反射调用
		Class<?> threadClazz = Class.forName(packetPath + className);
		Method method = threadClazz.getMethod(staticMethodName, parameterTypes);
		method.invoke(null, args);
	}


	/**
	 * 将传入的元素用指定的分隔符拼接成一个字符串
	 * 输出结果例: novasonic#10001#90000010001
	 * @param delimiter 分隔符
	 * @param elements  要拼接的元素
	 * @return 拼接后的字符串
	 */
	public static String joinStrWithDelimiter(String delimiter, Object... elements) {
		StringBuilder content = new StringBuilder();
		boolean firstElement = true;
		for (Object element : elements) {
			if (element != null && !element.toString().isEmpty()) {
				if (!firstElement) {
					content.append(delimiter);
				}
				content.append(element.toString());
				firstElement = false;
			}
		}
		return content.toString();
	}

	/**
	 * 根据 游戏ID取余*10*userId 去计算一个十六进制的邀请码
	 * @author hcy
	 * @date 2025年05月16日 17:29
	 * @param userId
	 * @param motuSdkGameId
	 * @return inviteCodeString 邀请码字符串
	 */
	public static String getHexStringInviteCode(long userId, int motuSdkGameId) {
		//这里游戏id取余0-99 不然可能出现long值过大
		int gameId = motuSdkGameId % 100;
		if (gameId > 0) {
			long inviteCode = gameId * 10 * userId;
			return Long.toHexString(inviteCode);
		} else {
			return Long.toHexString(userId);
		}
	}

	/**
	 * 根据十六进制的邀请码计算获取userId 根据游戏ID取余*10*userId
	 * @author hcy
	 * @date 2025年05月16日 17:30
	 * @param inviteCodeString 邀请码字符串
	 * @param motuSdkGameId sdk游戏Id
	 * @return userId 用户Id
	 */
	public static long getUserIdByHexStringInviteCode(String inviteCodeString, int motuSdkGameId) {
		long inviteCode = Long.parseLong(inviteCodeString, 16);
		int gameId = motuSdkGameId % 100;
		if (gameId > 0) {
			return inviteCode / 10 / gameId;
		} else {
			return inviteCode;
		}
	}

	/**
	 * 生成签名
	 * @param params 请求参数
	 * @param signKey 签名密钥
	 * @return 签名字符串
	 */
	public static String calculateSign(JsonObject params, String signKey) {
		try {
			// 将params转换为Map并移除sign字段
			Map<String, Object> paramMap = params.getMap();
			paramMap.remove("sign");

			// 将参数名按ASCII码排序
			List<String> keys = new ArrayList<>(paramMap.keySet());
			Collections.sort(keys);

			// 拼接参数
			StringBuilder sb = new StringBuilder();
			for (String key : keys) {
				Object value = paramMap.get(key);
				if (value != null && !value.toString().isEmpty()) {
					if (sb.length() > 0) {
						sb.append('&');
					}
					sb.append(key).append('=').append(value.toString());
				}
			}

			// 添加签名密钥
			sb.append(signKey);

			// 生成MD5签名
			String sign = DigestUtils.md5Hex(sb.toString());
			logger.debug("### Generate Sign. Params: {}, Sign: {}", sb, sign);
			return sign;
		} catch (Exception e) {
			logger.error("### Generate Sign Error: {}", Tool.getException(e));
			return null;
		}
	}

	/**
	 * 验证签名
	 * @param params 请求参数
	 * @param signKey 签名密钥
	 * @return 验证结果
	 */
	public static boolean verifySign(JsonObject params, String signKey) {
		try {
			String sign = params.getString("sign");
			if (sign == null || sign.isEmpty()) {
				return false;
			}

			String calculatedSign = calculateSign(params, signKey);
			return sign.equals(calculatedSign);
		} catch (Exception e) {
			logger.error("### Verify Sign Error: {}", Tool.getException(e));
			return false;
		}
	}

}

